package myky.com.doctorapp.utils;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.blankj.utilcode.util.LogUtils;
import com.google.gson.Gson;

import java.io.File;
import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import myky.com.doctorapp.api.Address;
import myky.com.doctorapp.contract.ImageUploadContract;
import myky.com.doctorapp.entity.ImageParamEntity;
import myky.com.doctorapp.entity.UploadResultEntity;
import myky.com.doctorapp.wight.SelfDialog;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import top.zibin.luban.Luban;
import top.zibin.luban.OnCompressListener;

/**
 * Description:
 *
 * @author Mr.li
 * Ctreated by 2019/2/20 14:35
 */
public class UploadImagesUtils {
    /**
     * 创建图片上传索引，保证图片顺序上传后不被打乱，根据序列存放图片路径
     */
    private Map<Integer, String> photoMaps = new HashMap<>(16);
    /**
     * 设置连接超时时间
     */
    private final static int CONNECT_TIMEOUT = 60;
    /**
     * 设置读取超时时间
     */
    private final static int READ_TIMEOUT = 120;
    /**
     * 设置写的超时时间
     */
    private final static int WRITE_TIMEOUT = 120;
    /**
     * 设置连接超时时间
     */
    private static OkHttpClient httpClient = new OkHttpClient().newBuilder().connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
            //设置读取超时时间
            .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
            //设置写的超时时间
            .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
            .build();
    static String BOUNDARY = "----------" + System.currentTimeMillis();
    public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("multipart/form-data;boundary=" + BOUNDARY);
    /**
     * 要求按加入顺序遍历输出
     */
    public static HashMap<Integer, String> hashMap;
    private static ImageUploadContract uploadOnListeners;
    private static List<ImageParamEntity> img_Lists;
    private static List<String> stringmd5;
    private static Boolean isSuccess;
    private static Context context;
    private static String numberType = "appId";
    private static String contentType = "模块来源";


    public static void Upload_Image(List<ImageParamEntity> img_List, final ImageUploadContract uploadOnListener, final Context contexts, final String numberTypes, final String contentTypes) {
        hashMap = new HashMap<>(16);
        stringmd5 = new ArrayList<>();
        img_Lists = new ArrayList<>();
        uploadOnListeners = uploadOnListener;
        ShareUtil.putData("isActiveClosed", false);
        isSuccess = false;
        context = contexts;
        numberType = numberTypes;
        contentType = contentTypes;
        //判断是否都是从我的病历获取的图片
        boolean hasPath = false;
        //判断是否需要上传图片
        img_Lists.addAll(img_List);
        for (int i = 0; i < img_Lists.size(); i++) {
            if (TextUtils.isEmpty(img_Lists.get(i).getPath())) {
                img_Lists.remove(i);
            }
        }
        if (img_Lists.size() > 0) {
            List<String> md5s = new ArrayList<>();
            for (int i = 0; i < img_Lists.size(); i++) {
                if (!img_Lists.get(i).isIs_md5()) {
                    hasPath = true;
                    md5s.add(img_Lists.get(i).getPath());
                } else {
                    md5s.add(SystemUtil.getPath_Md5(img_Lists.get(i).getPath()));
                }
            }
            if (hasPath) {
                for (int i = 0; i < img_Lists.size(); i++) {
                    //如果是md5值的就不用上传图片了用map保存起来
                    if (img_Lists.get(i).isIs_md5()) {
                        hashMap.put(i, SystemUtil.getPath_Md5(img_Lists.get(i).getPath()));
                    } else {
                        if (!ShareUtil.getBoolean("isActiveClosed")) {
                            CompreessPhoto(img_Lists.get(i).getPath(), i, uploadOnListener);
                        }
                    }
                }
            } else {
                //都是从我的病历获取的图片直接上传
                uploadOnListener.uploadSuccess(md5s);
            }
        } else {
            if (contentTypes.equals("预约")) {
                List<String> photos = new ArrayList<>();
                uploadOnListener.uploadSuccess(photos);
            } else {
                SelfDialog.dismissdialog();
                ToastUtils.showAtCenter("请至少上传一张图片");
            }
        }
    }

    /**
     * 图片压缩上传
     */

    private static void CompreessPhoto(final String path, final int position, final ImageUploadContract uploadOnListener) {
        Luban.with(context)
                // 传人要压缩的图片列表(包括file，path，List<String>)
                .load(path)
                // 忽略不压缩图片的大小
                .ignoreBy(100)
                //设置目标文件夹
                .setCompressListener(new OnCompressListener() {
                    @Override
                    public void onStart() {
                        // TODO 压缩开始前调用，可以在方法内启动 loading UI
                        Log.d("luban", "使用鲁班IO模式压缩,开始压缩");
                    }

                    @Override
                    public void onSuccess(File file) {
                        // TODO 压缩成功后调用，返回压缩后的图片文件
                        upload_img(file.getAbsolutePath(), position, uploadOnListener);
                        Log.i("output", "第" + position + "张图片压缩成功,开始上传");
                    }

                    @Override
                    public void onError(Throwable e) {
                        // TODO 当压缩过程出现问题时用原图上传
                        upload_img(path, position, uploadOnListener);
                        Log.i("output", "第" + position + "张图片压缩失败,使用原图片上传");
                    }
                }).launch(); //启动压缩
    }

    /**
     * 上传图片或MD5值
     */
    private static void upload_img(final String path, final int position, final ImageUploadContract uploadOnListener) {
        final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
        MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        httpClient = new OkHttpClient();
        File f = new File(path);
        long length = f.length();
        if (f != null) {
            builder.addFormDataPart("file", f.getName(), RequestBody.create(MEDIA_TYPE_PNG, f));
        }
        MultipartBody requestBody = builder.build();
        //构建请求
        final Request request = new Request.Builder()
                //地址
                .url(myky.com.doctorapp.api.Address.IMAGEUPLOADURL)
                //添加请求体
                .post(requestBody).tag(context)
                .build();
        httpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i("result", position + "------------failure");
                if (!e.toString().contains("closed")) {
                    if (e instanceof SocketTimeoutException) {
                        LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片上传超时，截取第" + (position + 1) + "张图片第一段上传");
                        cutFileUpload(0, path, 0l, "", position);
                    }
                    if (e instanceof ConnectException) {
                        uploadOnListeners.uploadFail("网络连接失败，请检查当前网络环境!");
                    }
                } else {
                    ShareUtil.putData("isActiveClosed", true);
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String result = response.body().string();
                Log.i("result", position + "------------success");
                LogUtils.i("md5", result);
                try {
                    Gson gson = new Gson();
                    UploadResultEntity UploadResultEntity = gson.fromJson(result, UploadResultEntity.class);
                    if (UploadResultEntity.getCode() == 0 && !UploadResultEntity.getData().equals("")) {
                        LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片上传成功");
                        hashMap.put(position, UploadResultEntity.getData());
                        isSuccessAll();
                    } else {
                        cutFileUpload(0, path, 0l, "", position);
                    }
                    //开始上传患者预约信息
                } catch (Exception e) {
                    e.printStackTrace();
                    uploadOnListener.uploadFail("图片上传服务异常");
                }
            }
        });
    }


    /**
     * 上传失败后分段上传第一段获取Md5值
     * @param bytes
     * @param filePath
     * @param nStart
     * @param length
     * @param times
     * @param position
     */
    private static void firstAppend(final byte[] bytes, final String filePath, final Long nStart, final Long length, int times, final int position) {
        final int[] firstTimeOutTimes = {times};
        MultipartBody.Builder builder = new MultipartBody.Builder()
                .setType(MultipartBody.FORM);
        RequestBody requestBody = RequestBody.create(MEDIA_TYPE_MARKDOWN, bytes);
        builder.addFormDataPart("file", filePath, requestBody);
        //构建请求
        final Request request = new Request.Builder()
                //地址
                .url(Address.IMAGEFIRSTAPPEND)
                //添加请求体
                .post(builder.build()).tag(context)
                .build();
        httpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                if (!e.toString().contains("closed") && !ShareUtil.getBoolean("isActiveClosed")) {
                    if (e instanceof SocketTimeoutException) {
                        //判断超时异常
                        if (firstTimeOutTimes[0] < 5) {
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片第一段上传超时,重新上传" + bytes);
                            firstTimeOutTimes[0]++;
                            firstAppend(bytes, filePath, nStart, length, firstTimeOutTimes[0], position);
                        } else {
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片第一段上传连续5次超时,上传失败");
                            uploadOnListeners.uploadFail("多次上传超时，请稍后再试!");
                        }
                    }
                    if (e instanceof ConnectException) {
                        ////判断连接异常，
                        uploadOnListeners.uploadFail("网络连接失败，请检查当前网络环境!");
                    }
                } else {
                    ShareUtil.putData("isActiveClosed", true);
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    String result = response.body().string();
                    Log.i("out", "onResponse." + result);
                    Gson gson = new Gson();
                    UploadResultEntity UploadResultEntity = gson.fromJson(result, UploadResultEntity.class);
                    if (UploadResultEntity.getCode() == 0 && !UploadResultEntity.getData().equals("")) {
                        if (nStart < length) {
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片第一段上传成功,开始截取后续文件上传--" + bytes);
                            cutFileUpload(1, filePath, nStart, UploadResultEntity.getData(), position);
                        } else {
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片上传成功");
                            hashMap.put(position, UploadResultEntity.getData());
                            isSuccessAll();
                        }
                    } else {
                        if (firstTimeOutTimes[0] < 5) {
                            firstTimeOutTimes[0]++;
                            firstAppend(bytes, filePath, nStart, length, firstTimeOutTimes[0], position);
                        } else {
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片第一段上传连续5次,上传失败失败");
                            uploadOnListeners.uploadFail("多次上传超时，请稍后再试!");
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    uploadOnListeners.uploadFail("图片上传服务异常");

                }
            }
        });
    }

    /**
     * 从第二段继续上传
     * @param bytes
     * @param filePath
     * @param md5
     * @param times
     * @param position
     * @param number
     * @param length
     */
    private static void appendUpload(final List<byte[]> bytes, final String filePath, final String md5, int times, final int position, final int number, final Long length) {
        final int[] i = {number};
        final int[] secondTimeOutTimes = {times};
        MultipartBody.Builder builder = new MultipartBody.Builder()
                .setType(MultipartBody.FORM);
        builder.addFormDataPart("file", filePath, RequestBody.create(MEDIA_TYPE_MARKDOWN, bytes.get(i[0])));
        builder.addFormDataPart("groupPath", md5);
        //构建请求
        final Request request = new Request.Builder()
                //地址
                .url(myky.com.doctorapp.api.Address.IMAGEAPPEND)
                //添加请求体
                .post(builder.build()).tag(context)
                .build();
        httpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i("uploadStatus", "failure");
                if (!e.toString().contains("closed") && !ShareUtil.getBoolean("isActiveClosed")) {
                    if (e instanceof SocketTimeoutException) {
                        //判断超时异常
                        if (secondTimeOutTimes[0] < 5) {
                            secondTimeOutTimes[0]++;
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片第" + (i[0] + 2) + "段上传超时,重新上传" + bytes.get(number));
                            appendUpload(bytes, filePath, md5, secondTimeOutTimes[0], position, number, length);
                        } else {
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片第" + (i[0] + 2) + "段上传连续5次超时,上传失败");
                            uploadOnListeners.uploadFail("多次上传超时，请稍后再试!");
                        }
                    }
                    if (e instanceof ConnectException) {
                        //判断连接异常，
                        uploadOnListeners.uploadFail("网络连接失败，请检查当前网络环境!");
                    }
                } else {
                    ShareUtil.putData("isActiveClosed", true);
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    String result = response.body().string();
                    Log.i("uploadStatus", "success" + result);
                    Gson gson = new Gson();
                    UploadResultEntity UploadResultEntity = gson.fromJson(result, UploadResultEntity.class);
                    if (UploadResultEntity.getCode() == 0 && !UploadResultEntity.getData().equals("")) {
                        i[0]++;
                        if (i[0] < (bytes.size())) {
                            appendUpload(bytes, filePath, md5, 0, position, i[0], length);
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片第" + (i[0] + 1) + "段上传成功---" + bytes.get(i[0] - 1));
                        } else {
                            hashMap.put(position, UploadResultEntity.getData());
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片第" + (i[0] + 1) + "段上传成功---" + bytes.get(i[0] - 1));
                            isSuccessAll();
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片上传成功,共" + (bytes.size() + 1) + "段---" + bytes.toString() + "长度为" + length);
                        }
                    } else {
                        if (secondTimeOutTimes[0] < 5) {
                            secondTimeOutTimes[0]++;
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片第" + (i[0] + 2) + "段上传失败,重新上传" + bytes.get(number));

                            appendUpload(bytes, filePath, md5, secondTimeOutTimes[0], position, number, length);
                        } else {
                            LogCollectionUtils.uploadLogs(context, numberType, "第" + (position + 1) + "张" + contentType + "图片第一段上传连续5次,上传失败失败");

                            uploadOnListeners.uploadFail("多次上传超时，请稍后再试!");
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    uploadOnListeners.uploadFail("图片上传服务异常");

                }
            }
        });
    }

    /**
     * 文件分割上传
     * @param operationType
     * @param filePath
     * @param nStartPos
     * @param md5First
     * @param position
     */
    public static void cutFileUpload(int operationType, String filePath, Long nStartPos, String md5First, int position) {
        try {
            FileAccessI fileAccessI = new FileAccessI(filePath, 0);
            Long length = fileAccessI.getFileLength();
            //每次处理500K数据
            int mBufferSize = 1024 * 500;
            byte[] buffer = new byte[mBufferSize];
            FileAccessI.Detail detail;
            long nRead = 0l;
            long nStart = nStartPos;
            int i = 0;
            switch (operationType) {
                //截取第一段
                case 0:
                    detail = fileAccessI.getContent(nStart);
                    nRead = detail.length;
                    buffer = detail.b;
                    nStart += nRead;
                    nStartPos = nStart;
                    if (!ShareUtil.getBoolean("isActiveClosed")) {
                        firstAppend(buffer, filePath, nStartPos, length, 0, position);
                    }
                    break;
                //截取剩下几段
                case 1:
                    List<byte[]> bytes = new ArrayList<>(16);
                    while (nStart < length) {
                        detail = fileAccessI.getContent(nStart);
                        nRead = detail.length;
                        buffer = detail.b;
                        nStart += nRead;
                        nStartPos = nStart;
                        bytes.add(buffer);
                    }
                    if (bytes.size() > 0) {
                        if (!ShareUtil.getBoolean("isActiveClosed")) {
                            appendUpload(bytes, filePath, md5First, 0, position, 0, length);
                        }
                    }
                    break;
                default:
            }
        } catch (Exception e) {

        }
    }


    /**
     * 判断是否完全上传
     */
    private static void isSuccessAll() {
        if (!isSuccess) {
            if (hashMap.size() == img_Lists.size() && hashMap.size() > 0) {
                isSuccess = true;
                stringmd5 = new ArrayList<>(16);
                LogCollectionUtils.uploadLogs(context, numberType, hashMap.size() + "张" + contentType + "图片全部上传成功");
                if (hashMap != null && hashMap.size() > 0) {
                    //将保存到map根据对应的下标设置MD5值
                    for (int i = 0; i < hashMap.size(); i++) {
                        stringmd5.add(hashMap.get(i));
                    }
                    if (!ShareUtil.getBoolean("isActiveClosed")) {
                        uploadOnListeners.uploadSuccess(stringmd5);
                    }
                }
            }
        }
    }

    /**
     * 根据Tag取消请求
     */
    public static void cancelThread(Object tag) {
        if (tag == null) return;
        for (Call call : httpClient.dispatcher().queuedCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
        for (Call call : httpClient.dispatcher().runningCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
    }
}
